Architecture of PHAD
To help myself understand how the heck PHAD works & how I can make it less confusing.
Dec 20, 2021 notes
-
Documentation::testSitemapGeneration()executes$phad->buildSitemap(), but phad internals never actually build sitemaps
AFTER REVIEWING THE CODEBASE, thoughts
Javascript FE
I read somewhere that I have onSubmit() handlers in the javascript, too ... but I don't see any javascript in here. Maybe that's a future issue??
Item Loading
it is hidden too deep in resolveAccess() and i find the flow very confusing. It works ... it's just hard to follow & keep my head around
Controller
It's really hard to tell who's responsible for what. The controller necessarily has many functions that are called during the display of a view. The controller contains a lot of the logic which should be handled by helper objects, or at least organized into traits.
Right now, Controller's job is to be a monolith that handles everything you throw at it. In the future, Controller's job will be to manage the state & communicate with other components. Refactoring this is probably first priority
And how do you override the controller? Like if you want to subclass it & provide custom validation functions?
I probably should make an interface for the controller ...
Compilation
Compilation is currently handled by the View class ... which is confusing & doesn't make a lot of sense. There's no clear way to just ... compile a whole folder, or straightforward way to just ... compile a single view.
The compiler itself is quite a monolith. It's almost 500 lines & it has several functions with VERY large bodies. This should probably be refactored so the code is easier to follow, possibly just into some traits. Though in past refactoring, I found it really difficult to change how the compiler worked because of all the nodes its dealing with & relationships and .... idunno. Refactoring the compiler is not a huge priority.
Currently, compilation can basically only be done by requesting the view that needs to be compiled. There's no other, more convenient way to do it.
Submission
There's too many interactions, moving parts ... idunno. It's just messy & needs to be cleaned up. I don't like, for example, that submit() calls 'isSubmissionValid()' creates a FormValidator, which then calls validate() ... like ... it's fine, but it feels so scattered. And there's no way to configure it without overriding the controller.
Other
- I need some directory structure in the
codefolder, probably - The way it was previously integrated with Liaison is kind of horrible to work with and terrible to understand
- View class: I don't like it. But ... it works & I don't think it really needs refactored ... I don't think
Viewis really the right name, since most of what it does is provide helper functions for NOT displaying html - Filters: I think it's fine, but i should provide more filters by default, including a
php:namespace filter that allows you to invoke any built-in function, likephp:strtoupper - Sitemap Building: Idk ... I ... don't know when/how it is invoked. Like I know I can build a sitemap, but ... how? And do I have to personally manage how often the sitemap is built? Need more clarity. I also don't know how sitemaps are delivered ... curently they compile to the
cachedir ... I don't think I ever set up routing for them.
NOTES from reviewing the codebase, below
Just ... notes
- Views are written in simple html using PHAD features like
<route>,item="ItemName"& more -
\Phad\Compilertakes in this clean html & produces a php + html output that is much more complex that makes all phad features work -
compiled html+php ... requires a
controllernamed$phad. So compiled output can beeval($compiled_output)as long as$phadis available. - A compiled view requires:
-
$phadobject (main controller) -
$phad_mode, to change the output/return value
-
- Phad can be used for routing & sitemap generation even if not using any database features
- filters
- form features (auto-fill, submit handler ...)
- attributes for:
- item nodes .... nodes with
item="Blog" - property nodes ... nodes with
prop="slug"...filter="ns:whatev" - route nodes ...
- sitemap nodes ...
- item nodes .... nodes with
- How an
item->listis built - actions (basically everything phad_mode allows)
- loading view ...
$lia->phad($view_name, $args)adds extra args & manages the compile step? - submitting form
- loading view ...
Classes
-
Addon(liaison integration, formerly compo):- adds global
phad()function for loading phad views - api to add sitemap handlers & property filters
- create a
Phad\Controller - create a
Phad\Item()object
- adds global
-
BlackHole: placeholder object for displaying a view without an object (so all props & methods return empty strings ... prevents errors) -
Compiler: Compiles clean phad-html into executable views -
Controller: central object in processing a compiled view for each for the phad_modes, loading item lists, verifying access, building sitemap, and communicating with other bits of software that do some other tasks. Basically, a bloated & unorganized mess -
Filter: Performs Default filtering ... currently only commonmark md to html -
FormValidator: Used byControllerto validate a submission. Validates based upon properties on the source node, liketype="number",required, minlength, maxlength, and others to come -
Package(liaison integration): initializes views, routes, and handles routing to a view -
PDOSubmitter: literally just saves submission to database & handles file upload. does NOT do any verification -
SitemapBuilder: a very simple utility class for writing sitemap xml to disk -
View: Loads the actual compiled view file & interfaces with the Compiler (convenience methods for each$phad_mode)
Default/Automatic $args
-
phad_submit_valueswill be set to$_POSTwhen a form is submitted (I think so anyway ...)
Submission flow
$this refers to the controller. Phad\Controller by default
- do view-flow up until the
submitstep - check
$this->onSubmit(...)&$this->isSubmissionValid(...)& proceed or return false - make a pdo submitter & submit() (just save to database if no pdo/mysql errors. uses LilDb)
- set lastInsertId on the controller &
idon the ItemRow - get the url to redirect to via
$this->getSubmitTarget($ItemData, $ItemRow) - redirect & return from the parent compiled-view
View flow
type refers to $phad_mode
$phad refers to the controller object passed to the compiled view.
- check if type is
get_routesorget_sitemap_data& return early if so - create the Item-info object that contains the access list, other paramaters, and will have it's list of items filled.
- check if type is
get_item_data& return the item from #2 if yes -
$phad->resolveAccess(item): Check if access to the view is granted AND load the item list ... ugh (its doing a lot)- loop over
$Item->accessList - check if
$this->hasAccess($Item, $Access_Row_as_object)- check if user-role passes. call
$this->getUser(), then$this->isUserInRole($user, string $role)(minimal helper functions that deserve better implementations) - check if
$Item->args['access.name']is set. if is set &$Access->nameis not the same, then return false. else return true - load the item list (empty object, submitted values, or values loaded from querying). For load values from query:
$this->getPdo(),$this->buildSql(...),$itemList = $pdo->fetchAll()...
- check if user-role passes. call
- set properties on
$Itemaccordingly for any passed access (like accessIndex & accessStatus)
- loop over
-
$phad->itemListStarted(item): ... internal item stack management - check
item->accessStatus- if
200, then continue displaying the view - else print message declared in
<on>nodes.
- if
- IF
<on>nodes declared inside<access>nodes, then checkitem->accessIndex& execute+display<on>contents accordingly - start
foreach($BlogItem->list as $BlogRow_Index=>$BlogRow): -
$Blog = $phad->objectFromRow($BlogItem, $BlogRow);: convert the row (from querying) into an object - if
!$phad->hasRowAccess($BlogItem, $Blog),continueto the next loop - check if type is
submit. if yes, and submission is successful, then return-
$phad->submit()... should invoke a handler???- your handler can set
$BlogItem->phad_mode = 'cancel_submit';to stop the submission???
- your handler can set
-
-
$phad->rowStarted($BlogItem, $Blog);: internal item stack management - execute/print the php+html inside the
item="Blog"node.- nodes with
prop="prop_name"declared will have php auto-inserted into them for getting the prop from the$Blogobject
- nodes with
-
$phad->rowFinished($BlogItem, $Blog): internal item stack management - run the rest of the
foreachloops -
$phad->itemListFinished($BlogItem);: internal item stack management
Controller
- user-ship
- has access
- loading items
- ...
Phad Nodes
-
<route pattern="/blog/{slug}/"></route> -
<sitemap sql="SELECT slug FROM blog" handler="ns:func" ...>: contains other declarations for the sitemap like priority, last_mod, changefreq, etc. thens:funcmust be registered withPhad\Addon::addSitemapHandler('ns:func', function(){})- must be inside a
<route>
- must be inside a
-
<access name="whatever" handler="ns:handler" role="user_role" where="Blog.slug LIKE :slug"> -
<on s=404>or<on s=200>: display different messages / run different code based upon the access results. CAN be inside an<access>to limit it's scope, or outside the access to apply to all accesses. -
<x-item item="Blog">a node that gets removed while allowing all PHAD features to work. -
<x-prop prop="title">a node that gets removed while allowing the prop to be printed -
<onsubmit>only for forms
Phad node properties
-
<input type="backend" name="slug">tellsphadthatslugwill be generated on the backend from other submitted values- does it forbid
slugfrom being submitted via$_POST???
- does it forbid
Handlers
- sitemap handler ...
- access handler ...
- filters
Compiled View
-
The Item an
Itemis created to hold information. If theitem="Blog", then there will be an object$BlogItem-
$BlogItem->nameisBlog, as declared initem="Blog" -
$BlogItem->accessList = []& has data added from<access>nodes, such as the query -
$BlogItem->accessStatusisfalse& is set200if access is granted,404if no items are found, (I think it does others, too) -
$BlogItem->accessIndexis the index in$BlogItem->accessListthat was approved for the current user -
$BlogItem->argsis the$argspassed in by$lia->view('view_name', [...args]) -
$BlogItem->phad_modeis$phad_mode??display -
$BlogItem->listWILL contain the array of items, if access is granted. This list is looped over to fill the view's html -
$BlogItem->propertiesONLY set for forms and contains information from the prop-nodes liketagName,type,minlength, and more
-
-
$phad_mode: pass to view to determine the response
-
display: default, will show the view -
get_routes: returns an array of routes, as an array of attributes from the<route>nodes -
get_sitemap_data: return array of sitemap data, as copied from the<sitemap>nodes -
submit: submits a form by calling$phad->submit($BlogItem, $BlogRow) -
get_item_data: load the$BlogItemas you would for displaying, but just return it. -
display_with_empty_objectwill display the form passing aBlackHoleinstance so all property values are empty
-